/*
 * F_fuse.c
 *
 *  Created on: 06/11/2011
 *      Author: tacho
 */

#include "../../headers/GlobalHeaderPFS.h"
#include "../../headers/Constantes_PFS.h"

extern pthread_mutex_t mutex_caches;
//extern struct_config configuracion;
extern tNodoCache * lCaches;
extern pthread_mutex_t mutex_fatRegion;


/*-------------------------------------------------------------------------*/
/**

 */
uint32_t pfs_truncar(const char * path, float tamano) {
	tDirectoryEntries entradasDelArchivo, entradasDelArchivoActualizadas;

	obtenerEntradaAPartirDePATH(path, &entradasDelArchivo);

	uint32_t clustersTotales = obtenerCantidadDeClusters(
			&(entradasDelArchivo.directoryEntry));
	uint32_t clustersNecesarios = ceil(tamano / TAMANO_CLUSTER);

	int32_t clustersAAgregarEliminar = clustersNecesarios - clustersTotales;
	uint32_t var;
	//si es menor a 0 debo quitar
	if (clustersAAgregarEliminar < 0) {
		for (var = 0; var < abs(clustersAAgregarEliminar); var++) {
			removerClusterAArchivo(&(entradasDelArchivo.directoryEntry));
		}
	} //sino debo agregar
	else {
		for (var = 0; var < abs(clustersAAgregarEliminar); var++) {
			agregarClusterAArchivo(&(entradasDelArchivo.directoryEntry));
		}
	}
	entradasDelArchivoActualizadas = entradasDelArchivo;
	entradasDelArchivoActualizadas.directoryEntry.size = tamano;
//	uint32_t cluster = obtenerDireccionPrimerCluster(
//			&(entradasDelArchivo.directoryEntry));
//	while ((fatRegion[cluster] != FAT_ULTIMO)
//			&& (fatRegion[cluster] != 0X0FFFFFF8)) {
//		cluster = fatRegion[cluster];
//	}
	reemplazarEntradaDelArchivo(path,&entradasDelArchivoActualizadas);
	return tamano;
}

/*-------------------------------------------------------------------------*/
/**

 */
uint32_t pfs_mover(const char * pathOrigen,const char * pathDestino){
	tDirectoryEntries entradaDir, entradaDirEliminar;

	obtenerEntradaAPartirDePATH(pathOrigen,&entradaDir);
	memcpy(&entradaDirEliminar,&entradaDir,sizeof(tDirectoryEntry));
	entradaDirEliminar.directoryEntry.name[0]=0xE5;
	entradaDirEliminar.longDirectoryEntry.orden=0xE5;
	reemplazarEntradaDelArchivo(pathOrigen,&entradaDirEliminar);
	char pathNuevoOrigen[50];
	eliminarUltimoSubPaths(pathDestino,pathNuevoOrigen);
	char nombreNuevo[20];
	ultimoPath(pathDestino,nombreNuevo);
	//tDirectoryEntries entradasDelDirectorioPadre;
	//obtenerEntradaAPartirDePATH(pathNuevoOrigen,&entradasDelDirectorioPadre);
	renombrarEntradas(nombreNuevo,&entradaDir);
	agregarDirectoryEntry(pathNuevoOrigen,&entradaDir);
	//free(pathNuevoOrigen);
	//free(nombreNuevo);
	return 0;
}

/*-------------------------------------------------------------------------*/
/**

 */
tStat pfs_getatri(tDirectoryEntries entradasArch){
		tStat archStat;
		memset(&archStat,0,sizeof(tStat));
		if (entradasArch.directoryEntry.atributos==0x10){
			archStat.st_size = entradasArch.directoryEntry.size;
			archStat.st_nlink = 2;
			archStat.st_mode = S_IFDIR | 0755;
		}else if (entradasArch.directoryEntry.atributos==0x20){
			archStat.st_size = entradasArch.directoryEntry.size;
			archStat.st_nlink = 1;
			archStat.st_mode = S_IFREG | 0444;;
			archStat.st_blksize=2048;
		}

		return archStat;
 }

/*-------------------------------------------------------------------------*/
/**

 */
uint32_t pfs_mkDir(const char * path){

	tDirectoryEntries entradasDir;
	memset(&entradasDir,0,sizeof(tDirectoryEntries));
	char nombre[20];
	ultimoPath(path,nombre);

	entradasDir.directoryEntry.atributos=0x10;
	//entradasDir.directoryEntry.size = TAMANO_CLUSTER;
	entradasDir.directoryEntry.size = 0;
	entradasDir.longDirectoryEntry.orden = 0x41;
	entradasDir.longDirectoryEntry.atributos = 0X0F;
	//entradasDir.longDirectoryEntry.firstcluster = 0XFFFF;
	renombrarEntradas(nombre,&entradasDir);
	uint32_t nroCluster;
	nroCluster = obtenerClusterLibre();
	pthread_mutex_lock(&mutex_fatRegion);
	reemplazarValorFat(nroCluster,FAT_ULTIMO);
	pthread_mutex_unlock(&mutex_fatRegion);
	agregarPrimerCluster(nroCluster,&entradasDir.directoryEntry);
	char pathPadre[50];
	eliminarUltimoSubPaths(path,pathPadre);
	agregarDirectoryEntry(pathPadre,&entradasDir);

	tDirectoryEntries entradaDirPadre;
	tCluster contenidoCluster;
	memset(&contenidoCluster,0,TAMANO_CLUSTER);
	//pathPadre = eliminarUltimoSubPaths(path);
	obtenerEntradaAPartirDePATH(pathPadre,&entradaDirPadre);
	uint32_t nroPadre = obtenerDireccionPrimerCluster(&entradaDirPadre.directoryEntry);
	if(nroPadre == 2)nroPadre = 0;
	agregarEntradasRelativas(&contenidoCluster,nroPadre,nroCluster);
	pedirEscrituraDeCluster(nroCluster,&contenidoCluster);
	return 0;
}

/*-------------------------------------------------------------------------*/
/**

 */
uint32_t pfs_mkFile(const char * path){

	tDirectoryEntries entradasDir;
	memset(&entradasDir,0,sizeof(tDirectoryEntries));
	char nombre[20];
	//memset(nombre,' ',20);
	ultimoPath(path,nombre);
	entradasDir.directoryEntry.atributos=0x20;
	entradasDir.directoryEntry.size = 0;
	entradasDir.longDirectoryEntry.orden = 0x41;
	entradasDir.longDirectoryEntry.atributos = 0X0F;
	entradasDir.longDirectoryEntry.firstcluster = 0XFFFF;
	renombrarEntradas(nombre,&entradasDir);
	agregarPrimerCluster(0,&entradasDir.directoryEntry);
//	uint32_t nroCluster;
//	nroCluster = obtenerClusterLibre();
//	reemplazarValorFat(nroCluster,FAT_ULTIMO);
//	agregarPrimerCluster(nroCluster,&entradasDir.directoryEntry);
	char pathPadre[50];
	eliminarUltimoSubPaths(path,pathPadre);
	agregarDirectoryEntry(pathPadre,&entradasDir);

	return 0;
}

/*-------------------------------------------------------------------------*/
/**

 */
uint32_t pfs_rmDir(const char * path){
	tDirectoryEntries entradaDirEliminar;
	entradaDirEliminar.directoryEntry.name[0]=0xE5;
	entradaDirEliminar.longDirectoryEntry.orden = 0xE5;
	pfs_truncar(path,0);
	reemplazarEntradaDelArchivo(path,&entradaDirEliminar);
	return 0;
}

/*-------------------------------------------------------------------------*/
/**

 */
uint32_t pfs_unlink(const char * path){
	tDirectoryEntries entradaDirEliminar;
	entradaDirEliminar.directoryEntry.name[0]=0xE5;
	entradaDirEliminar.longDirectoryEntry.orden = 0xE5;
	pfs_truncar(path,0);
	reemplazarEntradaDelArchivo(path,&entradaDirEliminar);
	return 0;
}

/*-------------------------------------------------------------------------*/
/**

 */
uint32_t pfs_read(const char * path,char * buffer,size_t tamano,off_t offset){
	tDirectoryEntries entradasArch;
	obtenerEntradaAPartirDePATH(path,&entradasArch);

	uint32_t tamanoALeer;
	if(offset > entradasArch.directoryEntry.size){
		return 0;
	}
	if (offset + tamano > entradasArch.directoryEntry.size){
		tamanoALeer = entradasArch.directoryEntry.size - offset;
	}else{
		tamanoALeer = tamano;
	}
	uint32_t cantClustersAAvanzar = ceil((float)offset/TAMANO_CLUSTER);
	uint32_t nroCluster = obtenerDireccionPrimerCluster(&entradasArch.directoryEntry);
	uint32_t i;
	for(i=1;i<=cantClustersAAvanzar;i++ ){
		nroCluster=obtenerSiguienteCluster(nroCluster);
	}
	uint32_t clusterInicio = nroCluster;
	uint32_t cantClustersALeer = ceil((float)tamanoALeer/TAMANO_CLUSTER);
	//nroCluster = obtenerClusterLeerXOffsetArch(nroCluster,offset/TAMANO_CLUSTER);
	uint32_t faltaLeer = tamanoALeer;
	uint32_t posArch= offset;
	uint32_t posBuffer=0;
	uint32_t nroBloque = ceil((offset - cantClustersAAvanzar*TAMANO_CLUSTER)/TAMANO_BLOQUE);
	uint32_t var,offsetEnCluster;
	uint32_t tamanoLeido,nroClusterAnt;
	tCluster contenidoCluster;
	uint32_t termine=0,offsetEnBloque;
	tBloque contenidoBloque;
	//buffer=(char *) malloc(tamano);
	//memset(buffer,0,tamano);
	//tnodoEntrada lista=NULL;
	//crearCadenaClusterAPartir(obtenerDireccionPrimerCluster(&entradasArch.directoryEntry),&lista);
	for (var=1;var<=cantClustersALeer;var++){
		if(nroCluster!=FAT_ULTIMO && nroCluster!=FAT_ULTIMO1){
			while((nroBloque<(TAMANO_CLUSTER/TAMANO_BLOQUE)) && termine==0){
				//pedirLecturaClusterArchs(path,nroCluster,&contenidoCluster);
				//pedirLecturaDeCluster(nroCluster,&contenidoCluster);
				pedirLecturaClusterArchs(path,(obtenerDireccionPrimerClusterDelDirectorioRaiz()+nroCluster-2)*BLOQUESXCLUSTER+nroBloque,&contenidoBloque);
				//offsetEnCluster = posArch % TAMANO_CLUSTER;
				offsetEnBloque = posArch % TAMANO_BLOQUE;
				if(faltaLeer > TAMANO_BLOQUE - offsetEnBloque){
					tamanoLeido=TAMANO_BLOQUE - offsetEnBloque;
				}else{
					tamanoLeido=faltaLeer;
				}
				memcpy(&(buffer[posBuffer]),&(contenidoBloque.bufferBloque[offsetEnBloque]),tamanoLeido);
				posArch+=tamanoLeido;
				posBuffer+=tamanoLeido;
				faltaLeer-=tamanoLeido;
				nroClusterAnt = nroCluster;
				nroBloque++;
				//printf("\n%d",nroCluster);
				if(faltaLeer == 0)termine=1;
			}
		nroCluster=obtenerSiguienteCluster(nroCluster);
		nroBloque = 0;
		}
	}
	//vaciarCadenaClusters(&lista);
		return posBuffer;
}

/*-------------------------------------------------------------------------*/
/**

 */
uint32_t pfs_write(const char *path, const char *buffer, size_t tamanoAEscribir, off_t offset){

	tDirectoryEntries entradaArch;
	obtenerEntradaAPartirDePATH(path,&entradaArch);

	if(offset + tamanoAEscribir > entradaArch.directoryEntry.size){
		pfs_truncar(path,offset+tamanoAEscribir);
		obtenerEntradaAPartirDePATH(path,&entradaArch);
	}
	uint32_t cantClustersAAvanzar;
	if((offset%TAMANO_CLUSTER)==0)cantClustersAAvanzar = ceil((float)offset/TAMANO_CLUSTER);
	else cantClustersAAvanzar = ceil((float)offset/TAMANO_CLUSTER)-1;
	uint32_t nroCluster = obtenerDireccionPrimerCluster(&entradaArch.directoryEntry);
	uint32_t i;
	for(i=1;i<=cantClustersAAvanzar;i++ ){
		nroCluster=obtenerSiguienteCluster(nroCluster);
	}

	uint32_t cantClustersALeer = ceil((float)tamanoAEscribir/TAMANO_CLUSTER);
	uint32_t faltaEscribir = tamanoAEscribir;
	uint32_t posArch = offset;
	uint32_t posBuffer = 0;
	uint32_t nroBloque = ceil((offset - cantClustersAAvanzar*TAMANO_CLUSTER)/TAMANO_BLOQUE);
	uint32_t var,offsetEnCluster;
	uint32_t tamanoEscrito;
	tCluster contenidoCluster;
	uint32_t termine=0,offsetEnBloque;
	tBloque contenidoBloque;
	for (var=0;var<cantClustersALeer;var++){
		if(nroCluster!=FAT_ULTIMO && nroCluster!=FAT_ULTIMO1){
			while((nroBloque<(TAMANO_CLUSTER/TAMANO_BLOQUE)) && termine==0){
			//pedirLecturaDeCluster(nroCluster,&contenidoCluster);
			//pedirLecturaClusterArchs(path,nroCluster,&contenidoCluster);
			pedirLecturaClusterArchs(path,(obtenerDireccionPrimerClusterDelDirectorioRaiz()+nroCluster-2)*BLOQUESXCLUSTER+nroBloque,&contenidoBloque);
			//offsetEnCluster = posArch % TAMANO_CLUSTER;
			offsetEnBloque = posArch % TAMANO_BLOQUE;
			if(faltaEscribir > TAMANO_BLOQUE - offsetEnBloque){
				tamanoEscrito= TAMANO_BLOQUE - offsetEnBloque;
			}else{
				tamanoEscrito=faltaEscribir;
			}
			//memcpy(&contenidoCluster.bufferCluster[offsetEnCluster],&buffer[posBuffer],tamanoEscrito);
			memcpy(&(contenidoBloque.bufferBloque[offsetEnBloque]),&(buffer[posBuffer]),tamanoEscrito);
			//pedirEscrituraClusterArchs(path,nroCluster,&contenidoCluster);
			//pedirEscrituraDeCluster(nroCluster,&contenidoCluster);
			pedirEscrituraClusterArchs(path,(obtenerDireccionPrimerClusterDelDirectorioRaiz()+nroCluster-2)*BLOQUESXCLUSTER+nroBloque,&contenidoBloque);
			posArch+=tamanoEscrito;
			posBuffer+=tamanoEscrito;
			faltaEscribir-=tamanoEscrito;
			nroBloque++;
			if(faltaEscribir == 0)termine=1;
			}
		nroCluster=obtenerSiguienteCluster(nroCluster);
		nroBloque = 0;
			}
	}
	return tamanoAEscribir;
}

/*-------------------------------------------------------------------------*/
/**

 */
uint32_t pfs_flush(const char * path){

	tNodoCache * nodoCache=conseguirCache(path);
	pthread_mutex_lock(&nodoCache->enUso);
	uint32_t var;
	for(var=0;var<nodoCache->cant;var++){
		borrarBloqueCache(nodoCache);
	}
	pthread_mutex_unlock(&nodoCache->enUso);
	return 1;
}

/*-------------------------------------------------------------------------*/
/**

 */
uint32_t pfs_release(const char * path){
	pthread_mutex_lock (&mutex_caches);
	tNodoCache * nodoCache=lCaches;
	tNodoCache * nodoAnt = NULL;
	int32_t encontre=0;
	if(!strcmp(path,nodoCache->path)){
		lCaches=lCaches->sgte;
	}else{
	while(encontre==0){
			if(!strcmp(path,nodoCache->sgte->path)){
			nodoAnt = nodoCache;
			nodoCache = nodoCache->sgte;
			nodoAnt->sgte=nodoCache->sgte;
			encontre=1;
			}else{
			nodoCache->sgte;
			}
		}
	}
	pthread_mutex_unlock (&mutex_caches);
	while(nodoCache->cache!=NULL){
		borrarBloqueCache(nodoCache);
	}

	free(nodoCache);
	nodoCache=NULL;

	return 1;

}
